package cn.ichiva.dl.tushare.analy;

import cn.ichiva.dl.tushare.common.AgentConfig;
import cn.ichiva.dl.tushare.common.Evolution;
import cn.ichiva.dl.tushare.common.ScoreAgent;
import org.nd4j.linalg.api.ndarray.INDArray;

import java.util.*;

/**
 * ai 排行榜
 */
public abstract class AgentList<T extends ScoreAgent> {

    private final int max;
    private TreeMap<Double,T> list = new TreeMap<>();

    public AgentList(Collection<T> list){
        this.max = list.size();
        list.forEach(e -> this.list.put(Integer.MIN_VALUE + Math.random(),e));
    }

    public ScoreAgent put(T agent){
        synchronized (list) {
            double score = getScore(agent);
            if (list.firstKey() > score) return null;

            list.put(score,agent);
            if(list.size() > this.max){
                Map.Entry<Double, T> entry = list.firstEntry();
                list.remove(entry.getKey());
                return entry.getValue();
            }
        }
        return null;
    }

    protected abstract double getScore(T agent);

    public Collection<T> getList(){
        return list.values();
    }

    /**
     * 从排行榜中参数新的ai
     * @return
     */
    public T getNewAgent(){
        double r = Math.random();
        //基因突变率
        double v = r / 11 + 0.01;
        //母本
        T mAgent = getMother(r);
        //父本
        T fAgent = getFather(r);

        int i = (int) (Math.random() * 3);
        INDArray newDNA = Evolution.defInstance.inheritance(mAgent.getDna(), fAgent.getDna());
        switch (i){
            case 0:
                newDNA = Evolution.defInstance.other(newDNA,v);
                break;
            case 1:
                newDNA = Evolution.defInstance.mutation(newDNA,v,0.1);
                break;
            case 2:
                newDNA = Evolution.defInstance.substitution(newDNA,v);
                break;
        }

        T agent = createAgent(mAgent.getConfig(), newDNA);
        agent.setGeneration(mAgent.getGeneration() + 1);
        return agent;
    }

    protected abstract T createAgent(AgentConfig config, INDArray newDNA);

    private T getMother(double r) {
        int index = (int) (r * max);
        return getAgent(index);
    }

    private T getFather(double r) {
        r += (Math.random() * (1-r));
        return getMother(r);
    }

    private T getAgent(int index) {
        synchronized (list) {
            Iterator<Map.Entry<Double, T>> it = list.entrySet().iterator();
            for (int i = 0; i < index; i++) {
                it.next();
            }
            return it.next().getValue();
        }
    }
}
